using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using Com.Guocz.Media.FileSystem.Crypt;

namespace Com.Guocz.Media.FileSystem
{
    public delegate void OutputFinished();
    public class OutputStream : AbstractOutputStream
    {
        public event OutputFinished onClose;
        private byte[] buf;
        private int cursor = 0;
        private Path path;
        private List<BlockMetaInfo> blockList;
        private DataManager data;
        private ICrypt cryptor;

        public OutputStream(Path f, DataManager dataManager, ICrypt cryptor)
        {
            path = f;
            buf = new byte[Const.BUFFER_SIZE];
            blockList = new List<BlockMetaInfo>();
            data = dataManager;
            this.cryptor = cryptor;
        }

        public override void Close()
        {
            Flush();
            path.InitBlockList(blockList);
            blockList.Clear();
            if (onClose != null)
            {
                onClose();
            }
        }

        public override void Flush()
        {
            int pos = 0;
            while (pos < cursor)
            {
                byte[] block = new byte[Math.Min(cursor - pos, Const.BLOCK_DATA_SIZE)];
                System.Array.Copy(buf, pos, block, 0, block.Length);
                pos += block.Length;
                if (cryptor != null)
                {
                    block = cryptor.Encrypt(block);
                }
                BlockMetaInfo blockMeta = data.write(block);
                blockList.Add(blockMeta);
            }

            cursor = 0;
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            while (count > 0)
            {
                if (GetFreeSize() == 0)
                {
                    Flush();
                }
                else
                {
                    int write = Math.Min(GetFreeSize(), count);
                    Array.Copy(buffer, offset, buf, cursor, write);
                    offset += write;
                    cursor += write;
                    count -= write;
                }
            }
        }

        private int GetFreeSize()
        {
            return buf.Length - cursor;
        }

        public override long Length
        {
            get { throw new NotImplementedException(); }
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotImplementedException();
        }

        public override void SetLength(long value)
        {
            throw new NotImplementedException();
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            if (blockList.Count > 0)
            {
                foreach (var item in blockList)
                {
                    data.Delete(item);
                }
            }
        }
    }
}
